
import { Component } from "cc";

/**冒泡排序 */
let bubbleSort = function (arr, fun) {
    let defaultCompare = function (a, b) {
        return a - b;
    }
    fun = fun || defaultCompare;
    for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr.length - i - 1; j++) {
            if (fun(arr[j], arr[j + 1]) <= 0) { // arr[j] 小于等于 arr[j + 1]，那么什么都不干

            } else { // a[j] 比 a[j + 1] 大， 那么交换
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
        }
    }
    return arr;
}

let printError = function (error: Error, des?: string) {
    if (des) {
        console.error(des);
    }
    if (error) {
        console.error("堆栈信息:" + error.stack);
    }
}

/**
 * 无返回值事件回调函数
 */
export type DispatchCallback = (...args: any) => void;

/**
 * 事件回调函数，返回是否拦截事件
 */
export type DispatchCallbackBool = (...args: any) => boolean;

/**
 * 事件类 观察者
 */
export class Observer {
    target: Object; // 关注事件的对象
    eventName: string | Object; // 事件名称
    callback: DispatchCallback | DispatchCallbackBool; // 事件回调函数
    widget: number = 0; // 权重
    constructor(target: Object, eventName: string | Object, callback: DispatchCallback | DispatchCallbackBool, widget: number) {
        this.target = target;
        this.eventName = eventName;
        this.widget = widget;
        this.callback = callback;
    }
}

let dispatchInstance: Dispatch = null;

/**
 * 一个简单的事件分发器，观察者模式，用于注册，分发事件
 */
export default class Dispatch {

    // 观察者数组
    protected readonly observers: Array<Observer> = new Array<Observer>();

    private _onDisable: boolean = false;
    constructor(onDisable: boolean) {
        this._onDisable = onDisable;
    }

    /**
     * 组件绑定的事件自动释放
     * @param target 
     */
    protected componentAutoOff(target: Component) {
        let _mark = target["_markDispatch"];
        if (!_mark) {
            //这个组件所注册的所有事件分发器
            let map: Map<Component, Set<Dispatch>> = new Map<Component, Set<Dispatch>>();
            map.set(target, new Set<Dispatch>([this]));
            target["_markDispatch"] = map;//将所有事件分发器联系上这个组件

            let onDestroy;
            if (target["onDestroy"]) {
                onDestroy = target["onDestroy"].bind(target);
            }
            let newOnDestroy = function () {
                if (onDestroy) {
                    try {
                        onDestroy();
                    } catch (error) {
                        printError(error, "Dispatch componentAutoOff onDestroy fail");
                    }
                }
                map.forEach((set: Set<Dispatch>, key: Component) => {
                    set.forEach((value: Dispatch) => {
                        value.off(key);//node销毁时，同时注销自己与这个分发器的链接
                    })
                });
                map.delete(target);
            }.bind(target);
            target["onDestroy"] = newOnDestroy;//替换新函数

            let onDisable;
            if (target["onDisable"]) {
                onDisable = target["onDisable"].bind(target);
            }

            let newOnDisable = function () {
                if (onDisable) {
                    try {
                        onDisable();
                    } catch (error) {
                        printError(error, "Dispatch componentAutoOff onDisable fail");
                    }
                }
                map.forEach((set: Set<Dispatch>, key: Component) => {
                    set.forEach((value: Dispatch) => {
                        if(value._onDisable){
                            value.off(key);//node销毁时，同时注销自己与这个分发器的链接
                        }
                    })
                });
                map.get(target)?.delete(this);
            }.bind(target);
            target["onDisable"] = newOnDisable;//替换新函数
        
        } else {
            let map: Map<Component, Set<Dispatch>> = _mark;
            map.set(target, map.get(target).add(this));//给这个组件增加一个事件分发器的链接
        }
    }

    /**
     * 添加观察事件, 同一观察对象，不能添加重复事件，后添加的将会覆盖前面添加的
     * @param target 
     * @param eventName 
     * @param callback 
     */
    on(target: Object | Component, eventName: string | Object, callback: DispatchCallback | DispatchCallbackBool, widget: number = 0) {
        if (target instanceof Component && !target.isValid) {
            return;
        }
        this.off(target, eventName);
        let observer = new Observer(target, eventName, callback, widget);
        this.observers.push(observer);
        if (target instanceof Component) {
            this.componentAutoOff(target);
        }
    }

    /**
     * 删除观察事件，返回删除的事件数量
     * @param target 
     * @param eventName 
     */
    off(target: Object, eventName?: string | Object) {
        let deleteCount = 0;
        for (let index = 0; index < this.observers.length;) {
            const observer = this.observers[index];
            if (observer.target === target && (!eventName || observer.eventName === eventName)) {
                // 删除
                this.observers.splice(index, 1);
                deleteCount++;
            } else {
                ++index;
            }
        }
        return deleteCount;
    }

    /**
     * 发射事件，并返回是否有通知到
     * @param eventName 事件名称
     * @param args  透传的参数
     */
    emit(eventName: string | Object, ...args: any) {
        let observers: Observer[] = [];//与本次事件有关的观察者
        for (let index = 0; index < this.observers.length; index++) {
            const observer = this.observers[index];
            if (observer.target instanceof Component && !observer.target.isValid) {
                continue;
            }
            if (observer.eventName === eventName) {
                observers.push(observer);
            }
        }
        //根据权重排序
        bubbleSort(observers, (oba: Observer, obb: Observer) => {
            return obb.widget - oba.widget;
        })
        for (let index = 0; index < observers.length; index++) {
            const observer = observers[index];
            // 如果返回的true表示拦截事件，如果拦截事件，那么直接返回
            try {
                if (observer.callback(...args)) {
                    return true;
                }
            } catch (error) {
                printError(error);
            }
        }
        return observers.length > 0;
    }

    /**
     * 获取观察事件数量
     */
    size() {
        return this.observers.length;
    }

    static getInstance() {
        if (null == dispatchInstance) {
            dispatchInstance = new Dispatch(false);
        }
        return dispatchInstance;
    }

}